package com.bdf.congcache.core.control.event;

import com.bdf.congcache.utils.threadpool.CongCacheThreadFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author 田培融
 * @Description  事件
 * @Date 16:47 2019/7/11
 **/
public class ElementEventQueue implements IElementEventQueue {

    private static final Log log = LogFactory.getLog(ElementEventQueue.class);

    private static final String THREAD_PREFIX = "CongCache-EventQueue-";

    private boolean destroyed = false;

    // 等待线程池 执行事件的任务
    private LinkedBlockingQueue<Runnable> queue;

    private ThreadPoolExecutor queueProcessor;


    /**
     * @description: 事件队列执行器
     * @author: 田培融
     * @date: 2019/7/21 15:34
     */
    public ElementEventQueue() {

        queue = new LinkedBlockingQueue<Runnable>();

        queueProcessor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue,
                new CongCacheThreadFactory(THREAD_PREFIX));
        if (log.isDebugEnabled()){
            log.debug("Element Event Queue construct: " + this);
        }
    }

    /**
     * @description:
     * @author: 田培融
     * @date: 2019/7/21 15:30
      * @param handler 事件执行器
     * @param event	 事件
     * @return: void
     */
    @Override
    public <T> void addElementEvent(IElementEventHandler handler, IElementEvent<T> event) throws IOException {
        if (log.isDebugEnabled())
        {
            log.debug("Add event handler to queue.");
        }
        if (destroyed)
        {
            log.warn("Event submitted to disposed element event queue " + event);
        }else
        {
            ElementEventRunner runner = new ElementEventRunner(handler, event);

            if (log.isDebugEnabled())
            {
                log.debug("Element event runner = " + runner);
            }

            queueProcessor.execute(runner);
        }
    }


    /**
     * @description:  事件执行器抽你类
     * @author: 田培融
     * @date: 2019/7/21 15:51
     */
    protected abstract class AbstractElementEventRunner implements Runnable
    {
        @Override
        public void run()
        {
            try
            {
                doRun();
            }
            catch (IOException e)
            {
                log.warn("Element event runner failure" + ElementEventQueue.this, e);
            }
        }
        protected abstract void doRun() throws IOException;
    }

    /**
     * @description: 事件执行器
     * @author: 田培融
     * @date: 2019/7/21 15:52
     */
    private class ElementEventRunner extends AbstractElementEventRunner
    {
        private final IElementEventHandler handler;

        private final IElementEvent<?> event;

        ElementEventRunner(IElementEventHandler hand, IElementEvent<?> event) throws IOException
        {
            if (log.isDebugEnabled())
            {
                log.debug("Element event runner constructed: " + this);
            }
            this.handler = hand;
            this.event = event;
        }

        @Override
        protected void doRun() throws IOException
        {
            handler.handleElementEvent(event);
        }
    }

    @Override
    public void dispose() {
        if (!destroyed){
            destroyed= true;
            queueProcessor.shutdownNow();
            queueProcessor =null;

            if (log.isInfoEnabled())
            {
                log.info("Element Event Queue destroy: " + this);
            }
        }
    }
}
